// This file is part of SyncStar, free software released under GPL v2.
package com.syncstar;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

import java.net.MalformedURLException;
import java.net.URL;

/** Interactive page for the configuration of the Android app. */
public class ConfigActivity extends Activity {
  @SuppressWarnings({"UnusedDeclaration"})
  private static final String TAG = ConfigActivity.class.getSimpleName();

  BroadcastReceiver broadcastReceiver_;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GlobalReceiver.onActivityCreate(this);

    // TODO: Design better layout, possibly copying from other apps.
    setTitle(R.string.config_title);
    setContentView(R.layout.config_layout);

    AccountManager manager = AccountManager.get(this);
    Account[] accounts = manager.getAccountsByType("com.google");
    String[] accStrings = new String[accounts.length + 1];
    for (int i = 0; i < accounts.length; ++i)
      accStrings[i] = accounts[i].name;
    accStrings[accounts.length] = "unset";

    SharedPreferences spConfig = getSharedPreferences("config", 0);

    CheckBox checkboxEnabled = (CheckBox)findViewById(R.id.background_sync_enabled);
    layoutCheckbox(checkboxEnabled);
    checkboxEnabled.setChecked(spConfig.getBoolean("is_bg_enabled", false));

    CheckBox checkboxPermissiveSsl = (CheckBox)findViewById(R.id.permissive_ssl);
    layoutCheckbox(checkboxPermissiveSsl);
    checkboxPermissiveSsl.setChecked(spConfig.getBoolean("permissive_ssl", false));

    // TODO: Show a separate dialog box for this setting so we don't have to
    //       update the "config" every time the user types a character.
    EditText edittextSyncInterval = (EditText)findViewById(R.id.edit_sync_interval);
    edittextSyncInterval.setText(spConfig.getInt("sync_interval_ms", SyncService.DEFAULT_SYNC_INTERVAL_MS) + " ms");

    // TODO: Show a separate dialog box for this setting so we don't have to
    //       update the "config" every time the user types a character.
    EditText edittextPopulateInterval = (EditText)findViewById(R.id.edit_populate_interval);
    edittextPopulateInterval.setText(spConfig.getInt("sync_populate_ms", PopulateService.DEFAULT_POPULATE_INTERVAL_MS) + " ms");

    EditText edittextPhone = (EditText)findViewById(R.id.edit_phone);
    edittextPhone.setText(spConfig.getString("phone", "ap"));

    EditText edittextSyncUrl = (EditText)findViewById(R.id.edit_sync_url);
    edittextSyncUrl.setText(SyncService.getSyncUrl(spConfig));

    Spinner spinnerAccounts = (Spinner)findViewById(R.id.accounts);
    ArrayAdapter<String> adapterAccounts = new ArrayAdapter<String>(
        this, android.R.layout.simple_spinner_item, accStrings);
    adapterAccounts.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinnerAccounts.setAdapter(adapterAccounts);

    updateWhyTextview();
    updateLastTextview();

    setUpHandlers();
  }

  private void layoutCheckbox(CheckBox checkbox) {
    final float scale = getResources().getDisplayMetrics().density;
    // This adds distance between the box and the text.
    checkbox.setPadding(checkbox.getPaddingLeft() + (int)(10.0 * scale + 0.5),
        checkbox.getPaddingTop(),
        checkbox.getPaddingRight(),
        checkbox.getPaddingBottom());
  }

  void updateWhyTextview() {
    String syncingState = SyncService.getSyncingState(this);
    String msg;
    if (!SyncService.SYNCING_NOTHING.equals(syncingState)) {
      msg = syncingState;
    } else {
      msg = SyncService.getWhyNoBackgroundSync(this);
      if (msg == null || "".equals(msg)) {
        msg = "Background sync could be scheduled, but it isn't -- maybe software bug?";
      }
    }
    ((TextView)findViewById(R.id.why)).setText(msg);
  }

  static long getAgoMs(SharedPreferences spLastsync, long now) {
    long agoMs = Long.MAX_VALUE;
    if (spLastsync.contains("last_with_response")) {
      if (spLastsync.contains("last_without_response")) {
        agoMs = now - Math.max(spLastsync.getLong("last_with_response", 0),
                               spLastsync.getLong("last_without_response", 0));
      } else {
        agoMs = now - spLastsync.getLong("last_with_response", 0);
      }
    } else if (spLastsync.contains("last_without_response")) {
      agoMs = now - spLastsync.getLong("last_without_response", 0);
    }
    return agoMs;
  }

  long getAgoMs() {
    return getAgoMs(getSharedPreferences("lastsync", 0),
                    System.currentTimeMillis());
  }

  void updateLastTextview() {
    long now = System.currentTimeMillis();
    SharedPreferences spLastsync = getSharedPreferences("lastsync", 0);
    long agoMs = getAgoMs(spLastsync, now);
    int currentRun = SyncService.getRun();
    StringBuilder sb = new StringBuilder();
    int run = spLastsync.getInt("run", SyncService.RUN_NOT_RUNNING);
    int status = spLastsync.getInt("status", SyncService.STATUS_UNKNOWN);
    if (agoMs == Long.MAX_VALUE) {
      if (currentRun == SyncService.RUN_NOT_RUNNING) {
        sb.append("Never synced.");
      } else {
        sb.append("Syncing for the first time.");
      }
    } else {
      // TODO: Indicate whether never synced successfully.
      if (currentRun == SyncService.RUN_NOT_RUNNING) {
        sb.append("Last sync was ");
      } else {
        sb.append("Previous sync was ");
      }
      StringBuilder sbAdj = new StringBuilder();
      if (status == SyncService.STATUS_SYNC_OK) {
        sbAdj.append("successful");
      } else {
        sbAdj.append("failed");
      }
      if (run == SyncService.RUN_MANUAL) {
        sbAdj.append(" manual");
      } else if (run == SyncService.RUN_SCHEDULED) {
        sbAdj.append(" background");
      } else if (run == SyncService.RUN_BACKGROUND) {
        sbAdj.append(" other backround");
      }
      sb.append(SyncService.formatArticle(sbAdj.toString()));
      sb.append(sbAdj.toString());
      sb.append(" sync ");
      sb.append(SyncService.formatAgo(agoMs));
      if (status != SyncService.STATUS_SYNC_OK) {
        sb.append(". It failed because ");
        sb.append(SyncService.formatLastReason(status));
      }
      sb.append(".");
    }
    ((TextView)findViewById(R.id.config_last)).setText(sb.toString());
  }

  /** Returns -1 on parse error.
   * @param s s
   * @return Time interval or -1 on parse error.
   */
  static int parseInterval(String s) {
    int j = s.indexOf(' ');  // TODO: Better ignore.
    if (j >= 0)
      s = s.substring(0, j);
    int i = -1;
    try {
      i = Integer.parseInt(s);
      if (i < 0)
        i = -1;
    } catch (NumberFormatException ignored) {
    }
    return i;
  }

  void setUpHandlers() {
    findViewById(R.id.configsync).setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
        if (SyncService.getRun() != SyncService.RUN_NOT_RUNNING)
          return;
        AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
          protected Void doInBackground(Void... params) {
            SyncService.doSync(ConfigActivity.this, SyncService.RUN_MANUAL,
                               getSharedPreferences("config", 0).getString("email", ""));
            return null;
          }
          protected void onPostExecute(Void statusObj) {
            // If this was the first manual sync, then schedule the background sync.
            SyncService.maybeStartOrStopBackgroundScheduled(ConfigActivity.this);
          }
        };
        //noinspection unchecked
        asyncTask.execute();
      }
    });

    Spinner spinnerAccounts = (Spinner)findViewById(R.id.accounts);
    spinnerAccounts.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
      public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
        String email = (String)adapterView.getItemAtPosition(position);
        SharedPreferences spConfig = getSharedPreferences("config", 0);
        SharedPreferences.Editor spConfigEditor = spConfig.edit();
        if (email == null || email.length() == 0 || "unset".equals(email)) {
          spConfigEditor.remove("email");
          spConfigEditor.remove("is_verified");
        } else {
          String oldEmail = spConfig.getString("email", null);
          if (!email.equals(oldEmail)) {
            spConfigEditor.putString("email", email);
            spConfigEditor.remove("is_verified");
          }
        }
        spConfigEditor.commit();
        SyncService.maybeStartOrStopBackgroundScheduled(ConfigActivity.this);
        updateWhyTextview();
        updateLastTextview();
      }

      public void onNothingSelected(AdapterView<?> adapterView) {
        // This cannot happen with a Spinner, but implement it anyway.
        getSharedPreferences("config", 0).edit()
            .remove("email").remove("is_verified").commit();
        SyncService.maybeStartOrStopBackgroundScheduled(ConfigActivity.this);
        updateWhyTextview();
        updateLastTextview();
      }
    });

    CheckBox checkboxEnabled = (CheckBox)findViewById(R.id.background_sync_enabled);
    checkboxEnabled.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
      public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
        getSharedPreferences("config", 0).edit().putBoolean("is_bg_enabled", isChecked).commit();
        SyncService.maybeStartOrStopBackgroundScheduled(ConfigActivity.this);
        // PopulateService.populatePresenceTableNowOrLater(ConfigActivity.this);
        updateWhyTextview();
        updateLastTextview();
      }
    });

    CheckBox checkboxPermissiveSsl = (CheckBox)findViewById(R.id.permissive_ssl);
    checkboxPermissiveSsl.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
      public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
        getSharedPreferences("config", 0).edit().putBoolean("permissive_ssl", isChecked).commit();
        // This may not take effect immediately, there may be some keep-alive
        // HTTPS connections already open.
        // TODO: Drop those connections.
        SslUtils.setPermissiveSslCertificateVerification(isChecked);
      }
    });

    EditText edittextSyncInterval = (EditText)findViewById(R.id.edit_sync_interval);
    edittextSyncInterval.addTextChangedListener(new TextWatcher() {
      public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }
      public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }
      public void afterTextChanged(Editable editable) {
        int i = parseInterval(editable.toString());
        if (i >= SyncService.MIN_SYNC_INTERVAL_MS) {
          getSharedPreferences("config", 0).edit().putInt("sync_interval_ms", i).commit();
          SyncService.maybeStartOrStopBackgroundScheduled(ConfigActivity.this);
        }
      }
    });

    EditText edittextPopulateInterval = (EditText)findViewById(R.id.edit_populate_interval);
    edittextPopulateInterval.addTextChangedListener(new TextWatcher() {
      public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }
      public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }
      public void afterTextChanged(Editable editable) {
        int i = parseInterval(editable.toString());
        if (i >= PopulateService.MIN_POPULATE_INTERVAL_MS) {
          getSharedPreferences("config", 0).edit().putInt("populate_interval_ms", i).commit();
          PopulateService.populatePresenceTableNowOrLater(ConfigActivity.this);
        }
      }
    });

    EditText edittextPhone = (EditText)findViewById(R.id.edit_phone);
    edittextPhone.addTextChangedListener(new TextWatcher() {
      public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }

      public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }

      public void afterTextChanged(Editable editable) {
        // TODO: Update only after the user has finished typing.
        String phone = editable.toString();
        if (phone == null || phone.length() == 0)
          phone = "ap";
        getSharedPreferences("config", 0).edit().putString("phone", phone).commit();
      }
    });

    EditText edittextSyncUrl = (EditText)findViewById(R.id.edit_sync_url);
    edittextSyncUrl.addTextChangedListener(new TextWatcher() {
      public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }

      public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
      }

      public void afterTextChanged(Editable editable) {
        // TODO: Update only after the user has finished typing.
        String syncUrl = editable.toString();
        try {
          new URL(syncUrl);
        } catch (MalformedURLException e) {
          syncUrl = null;
        }
        if (syncUrl == null || syncUrl.length() == 0)
          syncUrl = SyncService.DEFAULT_SYNC_URL;
        getSharedPreferences("config", 0).edit().putString("sync_url", syncUrl).commit();
      }
    });
  }

  void setUpBroadcastReceiver() {
    if (broadcastReceiver_ != null)
      unregisterReceiver(broadcastReceiver_);
    IntentFilter filter = new IntentFilter();
    filter.addAction(SyncService.PROPAGATE_TO_ACTIVITY);
    broadcastReceiver_ = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        // Log.e(TAG, "CONNX ConfigActivity receive");
        updateWhyTextview();
        updateLastTextview();
        setUpAgoAlarm();
      }
    };
    registerReceiver(broadcastReceiver_, filter);
  }

  void setUpAgoAlarm() {
    if (broadcastReceiver_ == null)
      return;
    Intent intent = new Intent(SyncService.PROPAGATE_TO_ACTIVITY);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
        this, SyncService.REQUEST_CODE_PROPAGATE, intent,
        PendingIntent.FLAG_UPDATE_CURRENT);
    long interval = SyncService.getAgoInterval(getAgoMs());
    AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
    // Log.e(TAG, "CONNX setupAgoAlarm interval=" + interval);
    alarmManager.set(
        AlarmManager.RTC, System.currentTimeMillis() + interval,
        pendingIntent);
  }

  void cancelAgoAlarm() {
    Intent intent = new Intent(SyncService.PROPAGATE_TO_ACTIVITY);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
        this, SyncService.REQUEST_CODE_PROPAGATE, intent,
        PendingIntent.FLAG_NO_CREATE);
    // Log.e(TAG, "CONNX cancelAgoAlarm wasSet=" + (pendingIntent != null));
    if (pendingIntent != null) {
      pendingIntent.cancel();
    }
  }

  /** Called when the activity becomes visible. */
  @Override
  public void onStart() {
    super.onStart();
    updateWhyTextview();
    updateLastTextview();
    setUpBroadcastReceiver();
    setUpAgoAlarm();
  }

  @Override
  public void onStop() {
    if (broadcastReceiver_ != null) {
      unregisterReceiver(broadcastReceiver_);
      broadcastReceiver_ = null;
    }
    cancelAgoAlarm();
    super.onStop();
  }
}